dojo.provide("dojox.mdnd.dropMode.DefaultDropMode");
dojo.require("dojox.mdnd.AreaManager");

dojo.declare("dojox.mdnd.dropMode.DefaultDropMode", null, {
	// summary:
	//		Enabled a type of calcul for Dnd.
	//		Default class to find the nearest target.

	// _oldXPoint: Integer
	//		used to save a X position 
	_oldXPoint: null,

	// _oldYPoint: Integer
	//		used to save a Y position
	_oldYPoint: null,

	// _oldBehaviour: String
	// 		see <getDragPoint>
	_oldBehaviour: "up",

	addArea: function(/*Array*/areas, /*Object*/object){
		// summary:
		//		Add a DnD Area into an array sorting by the x position.
		// areas:
		//		array of areas
		// object:
		//		data type of a DndArea
		// returns:
		//		a sorted area

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: addArea");
		var length =  areas.length;
		var position = dojo.position(object.node, true);
		object.coords = {'x':position.x, 'y':position.y};
		if (length == 0) {
			areas.push(object);
		}else{
			var x =  object.coords.x;
			for (var i = 0; i < length; i++) {
				if (x < areas[i].coords.x) {
					for (var j = length-1; j >= i; j--) 
						areas[j + 1] = areas[j];
					areas[i] = object;
					break;
				}
			}
			if (i == length) 
				areas.push(object);
		}
		return areas;	// Array
	},

	updateAreas: function(/*Array*/areaList){
		// summary:
		//		Refresh intervals between areas to determinate the nearest area to drop an item.
		//		Algorithm :
		//		the marker should be the vertical line passing by the
		//		central point between two contiguous areas.
		//		Note:
		//		If the page has only one targetArea, it's not necessary to calculate coords.
		// areaList:
		//		array of areas

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: initAreas");
		var length = areaList.length;
		if (length > 1){
			var currentRight, nextLeft;
			for (var i = 0; i < length; i++) {
				var area = areaList[i];
				var nextArea;
				area.coords.x1 = -1;
				area.coords.x2 = -1;
				if (i == 0) {
					nextArea = areaList[i+1];
					this._updateArea(area);
					this._updateArea(nextArea);
					currentRight = area.coords.x + area.node.offsetWidth;
					nextLeft =  nextArea.coords.x;	
					area.coords.x2 = currentRight + (nextLeft-currentRight)/2;
				}
				else if (i == length-1) {
					area.coords.x1 = areaList[i-1].coords.x2;
				}else{
					nextArea = areaList[i+1];
					this._updateArea(nextArea);
					currentRight = area.coords.x + area.node.offsetWidth;
					nextLeft =  nextArea.coords.x;
					area.coords.x1 = areaList[i-1].coords.x2;
					area.coords.x2 = currentRight + (nextLeft-currentRight)/2;
				}
			}
		}
	},

	_updateArea : function(/*Object*/area){
		// summary:
		//		update the DnD area object (i.e. update coordinates of its DOM node)
		// area:
		//		the DnD area
		// tags:
		//		protected

		//console.log("dojox.mdnd.dropMode.DefaultDropMode  ::: _updateArea");
		var position = dojo.position(area.node, true);
		area.coords.x = position.x;
		area.coords.y = position.y;
	},

	initItems: function(/*Object*/area){
		// summary:
		//		initialize the horizontal line in order to determinate the drop zone.
		// area:
		//		the DnD area

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: initItems");
		dojo.forEach(area.items, function(obj){
			//get the vertical middle of the item
			var node = obj.item.node;
			var position = dojo.position(node, true);
			var y = position.y + position.h/2;
			obj.y = y;
		});
		area.initItems = true;
	},

	refreshItems: function(/*Object*/area, /*Integer*/indexItem, /*Object*/size, /*Boolean*/added){
		// summary:
		//		take into account the drop indicator DOM element in order to compute horizontal lines
		// area:
		//		a DnD area object
		// indexItem:
		// 		index of a draggable item
		// size:
		//		dropIndicator size
		// added:
		//		boolean to know if a dropIndicator has been added or deleted

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: refreshItems");
		if (indexItem == -1) {
			return;
		}else if(area && size && size.h){
			var height = size.h;
			if (area.margin){
				height += area.margin.t;
			}
			var length = area.items.length;
			for (var i=indexItem; i<length; i++){
				var item = area.items[i];
				if (added) {
					item.y += height;
				}else{
					item.y -= height;
				}
			}
		}
	},

	getDragPoint: function(/*Object*/coords, /*Object*/size, /*Object*/mousePosition){
		// summary:
		//		return coordinates of the draggable item
		// description:
		//		return for: 
		// 			- X point : the middle 
		//	  		- Y point : search if the user goes up or goes down with his mouse.
		//	  		- Up : top of the draggable item
		//	  		- Down : bottom of the draggable item
		// coords:
		//		an object encapsulating X and Y position
		// size:
		//		an object encapsulating width and height values
		// mousePosition:
		//		coordinates of mouse
		// returns:
		//		an object of coordinates
		// 		example : {'x':10,'y':10}

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: getDragPoint");
		var y = coords.y;
		if (this._oldYPoint){
			if (y > this._oldYPoint) {
				this._oldBehaviour = "down";
				y += size.h;
			}
			else 
				if (y <= this._oldYPoint) {
					this._oldBehaviour = "up";
				}
		}
		this._oldYPoint = y;
		return { 
			'x': coords.x + (size.w / 2), 
			'y': y
			};	// Object
	},

	getTargetArea: function(/*Array*/areaList, /*Object*/ coords, /*integer*/currentIndexArea ){
		// summary:
		//		get the nearest DnD area.
		//		Coordinates are basically provided by the <getDragPoint> method.
		// areaList:
		//		a list of DnD areas objects
		// coords:
		//		coordinates [x,y] of the dragItem
		// currentIndexArea:
		//		an index representing the active DnD area
		// returns:
		//		the index of the DnD area

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: getTargetArea");
		var index = 0;
		var x = coords.x;
		var end = areaList.length;
		if (end > 1) {
			var start = 0, direction = "right", compute = false;
			if (currentIndexArea == -1 || arguments.length<3) {
				// first time : Need to search the nearest area in all areas.
				compute = true;
			}
			else {
				// check if it's always the same area
				if (this._checkInterval(areaList, currentIndexArea, x)){
					index = currentIndexArea;
				}else{
					if (this._oldXPoint < x){
						start = currentIndexArea + 1;
					}else{
						start = currentIndexArea - 1;
						end = 0;
						direction = "left";
					}
					compute = true;
				}
			}
			if (compute) {
				if (direction === "right") {
					for (var i = start; i < end; i++) {
						if (this._checkInterval(areaList, i, x)) {
							index = i;
							break;
						}
					}
				}else{
					for (var i = start; i >= end; i--) {
						if (this._checkInterval(areaList, i, x)) {
							index = i;
							break;
						}
					}
				}
			}
		}
		this._oldXPoint = x;
		return index;	// Integer
	},

	_checkInterval: function(/*Array*/areaList, /*Integer*/index, /*Coord*/x){
		// summary:
		//		check if the dragNode is in the interval.
		//		The x coordinate is basically provided by the <getDragPoint> method.
		// areaList:
		//		a list of DnD areas objects 	
		// index:
		//		index of a DnD area (to get the interval)
		// x:
		//		coordinate x, of the dragNode
		// returns:
		//		true if the dragNode is in intervall
		// tags:
		//		protected

		var coords = areaList[index].coords;
		if (coords.x1 == -1) {
			if (x <= coords.x2) {
				return true;
			}
		}
		else 
			if (coords.x2 == -1) {
				if (x > coords.x1) {
					return true;
				}
			}
			else {
				if (coords.x1 < x && x <= coords.x2) {
					return true;
				}
			}
		return false;	// Boolean
	},

	getDropIndex: function(/*Object*/ targetArea, /*Object*/ coords){
		// summary:
		//		Return the index where the drop has to be placed.
		// targetArea:
		//		a DnD area object
		// coords:
		//		coordinates [x,y] of the draggable item
		// returns:
		// 		a number
		//		or -1 if the area has no children or the drop index represents the last position in to the area

		//console.log("dojox.mdnd.dropMode.DefaultDropMode ::: getDropIndex");
		var length = targetArea.items.length;
		var coordinates = targetArea.coords;
		var y = coords.y;
		if (length > 0) {
			// course all children in the target area.
			for (var i = 0; i < length; i++) {
				// compare y value with y value of children
				if (y < targetArea.items[i].y) {
					return i;	// Integer
				}
				else {
					if (i == length-1) {
						return -1;
					}
				}
			}
		}
		return -1;
	},

	destroy: function(){
		//	can be overwritten.
	}
});

//------------
//Singleton	
//------------
(function(){
	dojox.mdnd.areaManager()._dropMode = new dojox.mdnd.dropMode.DefaultDropMode();
}());